{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1f0b9e91",
   "metadata": {},
   "source": [
    "| [02_advanced/07_面向对象编程.ipynb](https://github.com/shibing624/python-tutorial/blob/master/02_advanced/07_面向对象编程.ipynb)  | Python类  |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/02_advanced/07_面向对象编程.ipynb) |\n",
    "\n",
    "# 面向对象编程：OOP\n",
    "\n",
    "\n",
    "面向对象编程——Object Oriented Programming，简称OOP，是一种程序设计思想。OOP把对象作为程序的基本单元，一个对象包含了数据和操作数据的函数。\n",
    "\n",
    "在Python中，所有数据类型都可以视为对象，当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类（Class）的概念。\n",
    "\n",
    "\n",
    "## 类（Class）和实例（Instance）\n",
    "面向对象的设计思想是从自然界中来的，因为在自然界中，类（Class）和实例（Instance）的概念是很自然的。Class是一种抽象概念，比如我们定义的Class——Student，是指学生这个概念，而实例（Instance）则是一个个具体的Student，比如，张三和李四是两个具体的Student。\n",
    "\n",
    "所以，面向对象的设计思想是抽象出Class，根据Class创建Instance。\n",
    "\n",
    "面向对象的抽象程度又比函数要高，因为一个Class既包含数据，又包含操作数据的方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "113db9a4",
   "metadata": {},
   "source": [
    "## 创建类\n",
    "\n",
    "### 类的特殊方法\n",
    "Python 使用 __ 开头的名字来定义特殊的方法和属性，它们有：\n",
    "```\n",
    "__init__()\n",
    "__repr__()\n",
    "__str__()\n",
    "__call__()\n",
    "__iter__()\n",
    "__add__()\n",
    "__sub__()\n",
    "__mul__()\n",
    "__rmul__()\n",
    "__class__\n",
    "__name__\n",
    "```\n",
    "构造方法 `__init__()`\n",
    "\n",
    "在产生对象之后，我们可以向对象中添加属性。\n",
    "事实上，还可以通过构造方法，在构造对象的时候直接添加属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "b7d4a5b7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'green'"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class Clothes(object):\n",
    "    \"\"\"\n",
    "    init_demo\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, color=\"green\"):\n",
    "        self.color = color\n",
    "\n",
    "\n",
    "my_clothes = Clothes()\n",
    "my_clothes.color"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d969c85",
   "metadata": {},
   "source": [
    "传入有参数的值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "331ec959",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'orange'"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "your_clothes = Clothes('orange')\n",
    "your_clothes.color"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72f8113f",
   "metadata": {},
   "source": [
    "表示方法 `__repr__() 和 __str__()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "9e273c0b",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Clothes(object):\n",
    "    \"\"\"\n",
    "    repr and str demo\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, color=\"green\"):\n",
    "        self.color = color\n",
    "\n",
    "    def __str__(self):\n",
    "        \"This is a string to print.\"\n",
    "        return (\"a {} clothes\".format(self.color))\n",
    "\n",
    "    def __repr__(self):\n",
    "        \"This string recreates the object.\"\n",
    "        return (\"{}(color='{}')\".format(self.__class__.__name__, self.color))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "730501b7",
   "metadata": {},
   "source": [
    "`__str__()` 是使用 print 函数显示的结果,类似java中的toString："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "e35b1102",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a green clothes\n"
     ]
    }
   ],
   "source": [
    "my_clothes = Clothes()\n",
    "print(my_clothes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e17927e",
   "metadata": {},
   "source": [
    "`__repr__()` 返回的是不使用 print 方法的结果:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "b3ea284a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Clothes(color='green')"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_clothes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "02f5d187",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.Clothes'> Clothes green\n"
     ]
    }
   ],
   "source": [
    "print(my_clothes.__class__, my_clothes.__class__.__name__, my_clothes.color)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "0a04aa02",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(__main__.Clothes, 'Clothes', 'green')"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_clothes.__class__, my_clothes.__class__.__name__, my_clothes.color"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22bd1d54",
   "metadata": {},
   "source": [
    "## 类的属性\n",
    "只读属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "c74698cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Clothes(object):\n",
    "    def __init__(self, price):\n",
    "        self.price = price\n",
    "\n",
    "    # 这样 discount_price 就变成属性了\n",
    "    @property\n",
    "    def discount_price(self):\n",
    "        return self.price * 0.8"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b2ac70c",
   "metadata": {},
   "source": [
    "这里 discount_price 就是一个只读不写的属性了（注意是属性不是方法）,\n",
    "而price是可读写的属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "7595dfc3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "80.0\n"
     ]
    }
   ],
   "source": [
    "my_clothes = Clothes(100)\n",
    "print(my_clothes.discount_price)  # 80.0"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b37d38c",
   "metadata": {},
   "source": [
    "可以修改price属性来改变discount_price：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "648430c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "my_clothes.price = 200\n",
    "print(my_clothes.discount_price)  # 160.0"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1266aced",
   "metadata": {},
   "source": [
    "my_clothes.discount_price()会报错，因为 my_clothes.discount_price 是属性，不是方法；\n",
    "\n",
    "my_clothes.discount_price=100 也会报错，因为只读。\n",
    "\n",
    "\n",
    "对于 @property 生成的只读属性，我们可以使用相应的 @attr.setter 修饰符来使得这个属性变成可写的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "82ded4d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Clothes(object):\n",
    "    def __init__(self, price):\n",
    "        self.price = price\n",
    "\n",
    "    # 这样就变成属性了\n",
    "    @property\n",
    "    def discount_price(self):\n",
    "        return self.price * 0.8\n",
    "\n",
    "    @discount_price.setter\n",
    "    def discount_price(self, new_price):\n",
    "        self.price = new_price * 1.25"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89833c2b",
   "metadata": {},
   "source": [
    "测试一下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "f85cabc5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "80.0\n",
      "160.0\n"
     ]
    }
   ],
   "source": [
    "my_clothes = Clothes(100)\n",
    "print(my_clothes.discount_price)\n",
    "\n",
    "my_clothes.price = 200\n",
    "print(my_clothes.discount_price)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da826c79",
   "metadata": {},
   "source": [
    "修改 discount_price 属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "33633c49",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "225.0\n",
      "180.0\n"
     ]
    }
   ],
   "source": [
    "my_clothes.discount_price = 180\n",
    "print(my_clothes.price)\n",
    "print(my_clothes.discount_price)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0d51984",
   "metadata": {},
   "source": [
    "一个等价的替代如下，用方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "0dd459af",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Clothes(object):\n",
    "    def __init__(self, price):\n",
    "        self.price = price\n",
    "\n",
    "    def get_discount_price(self):\n",
    "        return self.price * 0.8\n",
    "\n",
    "    def set_discount_price(self, new_price):\n",
    "        self.price = new_price * 1.25\n",
    "\n",
    "    discount_price = property(get_discount_price, set_discount_price)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "d0c3ff71",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "80.0\n",
      "160.0\n",
      "225.0\n",
      "180.0\n"
     ]
    }
   ],
   "source": [
    "my_clothes = Clothes(100)\n",
    "print(my_clothes.discount_price)\n",
    "\n",
    "my_clothes.price = 200\n",
    "print(my_clothes.discount_price)\n",
    "\n",
    "my_clothes.discount_price = 180\n",
    "print(my_clothes.price)\n",
    "print(my_clothes.discount_price)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8dc75f1",
   "metadata": {},
   "source": [
    "## 继承\n",
    "\n",
    "类定义的基本形式：\n",
    "```python\n",
    "class ClassName(ParentClass):\n",
    "    \"\"\"class docstring\"\"\"\n",
    "    def method(self):\n",
    "        return\n",
    "```\n",
    "\n",
    "里面的 ParentClass 就是用来继承的。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "f6e1adea",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Clothes(object):\n",
    "    def __init__(self, color=\"green\"):\n",
    "        self.color = color\n",
    "\n",
    "    def out_print(self):\n",
    "        return self.__class__.__name__, self.color"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "087a6a7c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'green'"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_clothes = Clothes()\n",
    "my_clothes.color"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "011eb45c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('Clothes', 'green')"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_clothes.out_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62b81e5c",
   "metadata": {},
   "source": [
    "定义一个子类，继承父类的所有方法:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "897c345f",
   "metadata": {},
   "outputs": [],
   "source": [
    "class NikeClothes(Clothes):\n",
    "    def change_color(self):\n",
    "        if self.color == \"green\":\n",
    "            self.color = \"red\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5bb0aff3",
   "metadata": {},
   "source": [
    "继承父类的所有方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "2bb21dae",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'green'"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "your_clothes = NikeClothes()\n",
    "your_clothes.color"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "56cd3363",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('NikeClothes', 'green')"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "your_clothes.out_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1435585",
   "metadata": {},
   "source": [
    "但有自己的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "0ffaaed9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'red'"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "your_clothes.change_color()\n",
    "your_clothes.color"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dc6ae23",
   "metadata": {},
   "source": [
    "如果想对父类的方法进行修改，只需要在子类中重定义这个类即可："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "84cf7e48",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "green\n",
      "black\n",
      "('AdidasClothes', 'black')\n"
     ]
    }
   ],
   "source": [
    "class AdidasClothes(Clothes):\n",
    "    def change_color(self):\n",
    "        if self.color == \"green\":\n",
    "            self.color = \"black\"\n",
    "\n",
    "    def out_print(self):\n",
    "        self.change_color()\n",
    "        return self.__class__.__name__, self.color\n",
    "\n",
    "\n",
    "him_clothes = AdidasClothes()\n",
    "print(him_clothes.color)\n",
    "\n",
    "him_clothes.change_color()\n",
    "print(him_clothes.color)\n",
    "print(him_clothes.out_print())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2dab2ad9",
   "metadata": {},
   "source": [
    "### super() 函数\n",
    "super(CurrentClassName, instance)\n",
    "\n",
    "返回该类实例对应的父类对象。\n",
    "\n",
    "刚才 AdidasClothes可以改写为："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "521be22c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "green\n",
      "('NewAdidasClothes', 'black')\n"
     ]
    }
   ],
   "source": [
    "class NewAdidasClothes(Clothes):\n",
    "    def change_color(self):\n",
    "        if self.color == \"green\":\n",
    "            self.color = \"black\"\n",
    "\n",
    "    def out_print(self):\n",
    "        self.change_color()\n",
    "        print(super(NewAdidasClothes, self).out_print())\n",
    "\n",
    "her_clothes = NewAdidasClothes()\n",
    "print(her_clothes.color)\n",
    "\n",
    "her_clothes.out_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01623cfa",
   "metadata": {},
   "source": [
    "# 接口\n",
    "\n",
    "接口的调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "5c229531",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Clothes(object):\n",
    "    def __init__(self, color=\"green\"):\n",
    "        self.color = color\n",
    "\n",
    "    def out(self):\n",
    "        print(\"father.\")\n",
    "\n",
    "\n",
    "class NikeClothes(Clothes):\n",
    "    def out(self):\n",
    "        self.color = \"brown\"\n",
    "        super(NikeClothes, self).out()\n",
    "\n",
    "\n",
    "class AdidasClothes(object):\n",
    "    def out(self):\n",
    "        print(\"adidas.\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68018094",
   "metadata": {},
   "source": [
    "因为三个类都实现了 out() 方法，因此可以这样使用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "81fc0005",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "father.\n",
      "father.\n",
      "adidas.\n"
     ]
    }
   ],
   "source": [
    "objects = [Clothes(), NikeClothes(), AdidasClothes()]\n",
    "for obj in objects:\n",
    "    obj.out()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38f3af6b",
   "metadata": {},
   "source": [
    "## 类方法\n",
    "类方法包括以下几种：\n",
    "1. special 方法和属性，即以 __ 开头和结尾的方法和属性\n",
    "2. 私有方法和属性，以 _ 开头，不过不是真正私有，而是可以调用的，\n",
    "但是不会被代码自动完成所记录（即 Tab 键之后不会显示）\n",
    "3. 共有的方法和属性\n",
    "\n",
    "\n",
    "以 `__` 开头不以 `__` 结尾的属性是更加特殊的方法，调用方式也不同："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "8b3ea0c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyDemoClass(object):\n",
    "    def __init__(self):\n",
    "        print(\"special.\")\n",
    "\n",
    "    def _get_name(self):\n",
    "        print(\"_get_name is private method.\")\n",
    "\n",
    "    def get_value(self):\n",
    "        print(\"get_value is public method.\")\n",
    "\n",
    "    def __get_type(self):\n",
    "        print(\"__get_type is really special method.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "34a79634",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "special.\n"
     ]
    }
   ],
   "source": [
    "demo = MyDemoClass()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "e808b9a7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get_value is public method.\n",
      "_get_name is private method.\n",
      "__get_type is really special method.\n"
     ]
    }
   ],
   "source": [
    "demo.get_value()\n",
    "demo._get_name()\n",
    "demo._MyDemoClass__get_type()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52342a2d",
   "metadata": {},
   "source": [
    "本节完。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10b33ab2",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}